/**
 * 2025/01/01 - 2025/12/31
 */
package com.snail.server.database.engine.ddl.table.manager;

import com.snail.server.database.engine.ddl.field.dto.FieldAddDTO;
import com.snail.server.database.engine.ddl.field.entity.Field;
import com.snail.server.database.engine.ddl.field.manager.FieldManager;
import com.snail.server.database.engine.ddl.field.mapping.FieldMapping;
import com.snail.server.database.engine.ddl.field.service.FieldService;
import com.snail.server.database.engine.ddl.field.vo.FieldVO;
import com.snail.server.database.engine.ddl.holder.DDLHolder;
import com.snail.server.database.engine.ddl.index.dto.IndexAddDTO;
import com.snail.server.database.engine.ddl.index.dto.IndexFieldDTO;
import com.snail.server.database.engine.ddl.index.entity.Index;
import com.snail.server.database.engine.ddl.index.entity.IndexFieldMapping;
import com.snail.server.database.engine.ddl.index.manager.IndexManager;
import com.snail.server.database.engine.ddl.index.mapping.IndexMapping;
import com.snail.server.database.engine.ddl.index.service.IndexFieldMappingService;
import com.snail.server.database.engine.ddl.index.service.IndexService;
import com.snail.server.database.engine.ddl.index.vo.IndexFieldVO;
import com.snail.server.database.engine.ddl.index.vo.IndexVO;
import com.snail.server.database.engine.ddl.model.table.TableCreate;
import com.snail.server.database.engine.ddl.table.dto.TableAlterDTO;
import com.snail.server.database.engine.ddl.table.dto.TableCreateDTO;
import com.snail.server.database.engine.ddl.table.entity.Table;
import com.snail.server.database.engine.ddl.table.mapping.TableMapping;
import com.snail.server.database.engine.ddl.table.service.TableService;
import com.snail.server.database.engine.ddl.table.vo.TableVO;
import com.snail.starter.db.holder.DynamicTransactionTemplate;
import com.snail.starter.utils.id.ObjectId;
import com.snail.starter.utils.lambda.GenericEntityBuilder;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author zgadmin
 * @date 2025/8/29 14:34
 */
@Component
public class TableManager {

    @Resource
    DDLHolder ddlHolder;

    @Resource
    TableService tableService;

    @Resource
    FieldService fieldService;

    @Resource
    IndexService indexService;

    @Resource
    IndexFieldMappingService indexFieldMappingService;

    @Resource
    DynamicTransactionTemplate dynamicTransactionTemplate;

    @Resource
    TableMapping tableMapping;

    @Resource
    FieldMapping fieldMapping;

    @Resource
    IndexMapping indexMapping;

    public Long create(Long dataSourceId, TableCreateDTO tableCreateDTO) {
        Table table = tableMapping.tableCreateDTO2Table(tableCreateDTO);
        table.setDataSourceId(dataSourceId);
        table.setTableId(ObjectId.nextLongId());
        List<Field> fields = toField(table.getTableId(), tableCreateDTO.getFields());
        List<Index> indices = toIndex(table.getTableId(), tableCreateDTO.getIndexes());
        List<IndexFieldMapping> indexFieldMappings = toIndexField(table.getTableId(), fields, tableCreateDTO.getIndexes());
        TableCreate tableCreate = GenericEntityBuilder.of(TableCreate::new)
                .withSupplier(TableCreate::setName, tableCreateDTO::getName)
                .withSupplier(TableCreate::setDescription, tableCreateDTO::getDescription)
                .withSupplier(TableCreate::setFields, () -> FieldManager.toFieldMo(tableCreateDTO.getFields()))
                .withSupplier(TableCreate::setIndexes, () -> IndexManager.toIndexMo(tableCreateDTO.getIndexes()))
                .build();
        return dynamicTransactionTemplate.doTransaction(status -> {
            tableService.save(table);
            if (null != fields && !fields.isEmpty()) {
                fieldService.saveBatch(fields);
            }
            if (null != indices && !indices.isEmpty()) {
                indexService.saveBatch(indices);
            }
            if (null != indexFieldMappings && !indexFieldMappings.isEmpty()) {
                indexFieldMappingService.saveBatch(indexFieldMappings);
            }
            ddlHolder.createTable(dataSourceId, tableCreate);
            return table.getTableId();
        });
    }

    public boolean alter(Long datasourceId, Long tableId, TableAlterDTO tableAlter) {
        return dynamicTransactionTemplate.doTransaction(status -> ddlHolder.alterTable(datasourceId, null));
    }

    public boolean delete(Long datasourceId, Long tableId) {
        Table table = tableService.checkById(datasourceId, tableId);
        return dynamicTransactionTemplate.doTransaction(status -> {
            tableService.dropTable(datasourceId, tableId);
            fieldService.dropTable(tableId);
            indexService.dropTable(tableId);
            return ddlHolder.dropTable(datasourceId, table.getName());
        });
    }

    public TableVO queryById(Long datasourceId, Long tableId) {
        Table table = tableService.checkById(datasourceId, tableId);
        TableVO tableVO = tableMapping.toTableVO(table);
        List<Field> fields = fieldService.queryByTable(tableId);
        tableVO.setFields(toFieldVo(fields));
        tableVO.setIndices(toIndexVo(indexService.queryByTable(tableId),
                        indexFieldMappingService.queryByTable(tableId),
                        fields.stream()
                                .collect(Collectors.toMap(Field::getFieldId, Field::getName, (o1, o2) -> o2))
                )
        );
        return tableVO;
    }

    public List<IndexVO> toIndexVo(List<Index> indices, List<IndexFieldMapping> indexFieldMappings, Map<Long, String> fieldMap) {
        if (null == indices || indices.isEmpty() || null == indexFieldMappings || indexFieldMappings.isEmpty() || null == fieldMap || fieldMap.isEmpty()) {
            return new ArrayList<>();
        }
        Map<Long, List<IndexFieldMapping>> indexFieldMap = indexFieldMappings.stream().collect(Collectors.groupingBy(IndexFieldMapping::getIndexId));
        return indices.stream().map(index -> {
            IndexVO indexVO = indexMapping.toIndexVO(index);
            List<IndexFieldMapping> fieldMappings = indexFieldMap.get(indexVO.getIndexId());
            if (null == fieldMappings || fieldMappings.isEmpty()) {
                return indexVO;
            }
            List<IndexFieldVO> indexFieldList = fieldMappings.stream().map(indexFieldMapping -> {
                String fieldName = fieldMap.get(indexFieldMapping.getFieldId());
                if (null == fieldName || "".equals(fieldName)) {
                    return null;
                }
                IndexFieldVO indexFieldVO = new IndexFieldVO();
                indexFieldVO.setFieldName(fieldName);
                indexFieldVO.setOrderBy(indexFieldMapping.getOrderBy().getValue());
                return indexFieldVO;
            }).filter(Objects::nonNull).distinct().collect(Collectors.toList());
            indexVO.setIndexFields(indexFieldList);
            return indexVO;
        }).collect(Collectors.toList());
    }

    public List<FieldVO> toFieldVo(List<Field> fields) {
        if (null == fields || fields.isEmpty()) {
            return new ArrayList<>();
        }
        return fields.stream().map(field -> fieldMapping.toFieldVO(field)).collect(Collectors.toList());
    }

    public List<Field> toField(Long tableId, List<FieldAddDTO> fields) {
        if (null == fields || fields.isEmpty()) {
            return new ArrayList<>();
        }
        return fields.stream()
                .map(fieldAddDTO -> {
                    Field field = fieldMapping.fieldAddDTO2Field(tableId, fieldAddDTO);
                    field.setFieldId(ObjectId.nextLongId());
                    return field;
                })
                .distinct()
                .collect(Collectors.toList());
    }

    public List<Index> toIndex(Long tableId, List<IndexAddDTO> indexes) {
        if (null == indexes || indexes.isEmpty()) {
            return new ArrayList<>();
        }
        return indexes.stream()
                .map(indexAddDTO -> indexMapping.toIndex(tableId, ObjectId.nextLongId(), indexAddDTO))
                .collect(Collectors.toList());

    }

    public List<IndexFieldMapping> toIndexField(Long tableId, List<Field> fields, List<IndexAddDTO> indexDtoList) {
        if (null == fields || fields.isEmpty()) {
            return new ArrayList<>();
        }
        List<IndexFieldDTO> indexFields = indexDtoList.stream()
                .flatMap(indexAddDTO -> indexAddDTO.getIndexFields().stream())
                .collect(Collectors.toList());
        if (null == indexFields || indexFields.isEmpty()) {
            return new ArrayList<>();
        }
        Map<String, Long> fieldMap = fields.stream()
                .collect(Collectors.toMap(Field::getName, Field::getFieldId, (o1, o2) -> o1));
        return indexFields.stream()
                .map(indexFieldDTO -> {
                    Long fieldId = fieldMap.get(indexFieldDTO.getFieldName());
                    if (null == fieldId) {
                        return null;
                    }
                    return indexMapping.toIndexFieldMapping(tableId, ObjectId.nextLongId(), fieldId, indexFieldDTO);
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

}